package com.manager.common.aspect;

import com.manager.common.annotation.AspectLogInfo;
import com.manager.common.annotation.OperationAssist;
import com.manager.common.constant.OperationAssistSystemContants;
import com.manager.common.token.TokenUtils;
import com.manager.common.utils.ClassUtils;
import com.manager.common.utils.DateTools;
import com.manager.model.Operation_Log;
import com.manager.model.System_User;
import com.manager.system.service.OperateLogService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;

@Aspect
@Component
@SuppressWarnings("all")
public class FrameworkOperationLogAroundAspect implements Ordered{

	@Override
	public int getOrder() {
		// TODO Auto-generated method stub
		return 2;
	}
	@Autowired
	private OperateLogService operateLogServiceImp;

	@Pointcut("execution(public * com.manager.*.controller.*.*(..))")
	private void controllerAspect() {
	}
	
	@Around(value = "controllerAspect()")
	public Object timeAround(ProceedingJoinPoint joinPoint) throws Exception {
		// 定义返回对象、得到方法需要的参数
		ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
				.getRequestAttributes();

		HttpServletResponse response = requestAttributes.getResponse();
		HttpServletRequest request = requestAttributes.getRequest();
		MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

		Method method = methodSignature.getMethod();

		Object obj = null;
		Object[] args = joinPoint.getArgs();
		long startTime = System.currentTimeMillis();
		boolean mark = true;
		System_User user = null;
		try {
			obj = joinPoint.proceed(args);

			user = TokenUtils.getLoginUser();
		} catch (Throwable e) {
			e.printStackTrace();
			mark = false;
		}

		long endTime = System.currentTimeMillis();

		AspectLogInfo log = method.getAnnotation(AspectLogInfo.class);

		if (log != null && user != null ) {

			Operation_Log model = new Operation_Log();

			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

			model.setLogType(log.logType().getvalue());
			model.setUserId(user.getId());
			model.setCreateTime(DateTools.getCurrentDateLong());
			model.setShowTime(DateTools.getCurrentDateTime());
			model.setOperationContent(log.content());
			model.setOperationType(log.type().getCnDesc());
			model.setTableName(log.tableName());
			model.setUserName(user.getUserName());

			model.setId(UUID.randomUUID().toString().replaceAll("-", ""));
			model.setStartTime(format.format(new Date(startTime)));
			model.setEndTime(format.format(new Date(endTime)));
			model.setProceedTime(String.valueOf(endTime - startTime));
			
			if (log.assist()) {
				List<String> list = new ArrayList<>();
				if (!log.isUseResult()) {
					List<Class> clazList = Arrays.asList(log.claz());
					list = getAllAssistContents(args, clazList);
					String contents = dealtAssistList(list);
					model.setAssistContents(contents);
				} else {
					getAssistContents(obj, list);
					String contents = dealtAssistList(list);
					model.setAssistContents(contents);
				}
				/*model.setDataId(String.valueOf(ClassUtils.invokeGet(obj, "id")));
				model.setParentId(getParentId(obj));*/
			}
			
			if (!mark) {
				model.setOperationResult("0");
			} else {
				model.setOperationResult("1");
			}

			if ("1".equals(user.getId())) {
				model.setRoleName("admin");
			} else {
				model.setRoleName(user.getString2());
			}
			
			if (StringUtils.isBlank(model.getIp()))
				model.setIp(request.getRemoteAddr());

			operateLogServiceImp.insertOperateLog(model);

		}
		
		return obj;
	}
	
	private String getParentId(Object ob) {
		if (ob == null) {
			return null;
		}
		List<Field> list = new ArrayList<>();
		Class<?> claz = ob.getClass();

		list.addAll(Arrays.asList(claz.getDeclaredFields()));

		for (Field f : list) {
			OperationAssist oa = f.getAnnotation(OperationAssist.class);
			if (oa != null && oa.preant()) {
				return String.valueOf(ClassUtils.invokeGet(ob, f.getName()));
			}
		}
		return null;
	}

	private String dealtAssistList(List<String> list) {
		String contents = "";
		if (list == null || list.size() == 0) {
			return contents;
		}

		for (int i = 0; i < list.size(); i++) {
			contents += list.get(i);
		}

		return contents;
	}

	private List<String> getAllAssistContents(Object[] objects, List<Class> clazList) {
		List<String> res = new ArrayList<>();
		for (Class claz : clazList) {
			for (Object ob : objects) {
				if (ob.getClass().getName().equals(claz.getName())) {
					getAssistContents(ob, res);
					break;
				}
			}
		}
		return res;
	}

	private void getAssistContents(Object ob, List<String> res) {
		if (ob == null) {
			return;
		}
		List<Field> list = new ArrayList<>();
		Class<?> claz = ob.getClass();

		list.addAll(Arrays.asList(claz.getDeclaredFields()));

		for (Field f : list) {
			OperationAssist oa = f.getAnnotation(OperationAssist.class);
			if (oa != null && oa.objType() != null && !"".equals(oa.objType())) {
				if (oa.objType().equals(OperationAssistSystemContants.ARRAY)) {
					res.add(oa.desc() + "->{");
					dealListObject(ob, f, res);
					res.add("}");
				} else if (oa.objType().equals(OperationAssistSystemContants.ONE)) {
					res.add("[");
					getAssistContents(ClassUtils.invokeGet(ob, f.getName()), res);
					res.add("]");
				} else if (oa.objType().equals(OperationAssistSystemContants.NORMAL)) {
					res.add(oa.desc().trim() + "->" + ClassUtils.invokeGet(ob, f.getName()) + " ");
				}
			}
		}
	}

	private void dealListObject(Object ob, Field field, List<String> result) {
		List list = (List) ClassUtils.invokeGet(ob, field.getName());
		if (list == null) {
			return;
		}
		for (Object res : list) {
			getAssistContents(res, result);
		}
	}

	
}
